package wikibooks;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Logger;

/**
 * Application entry point.
 */
public class LaTeXConverter {

    private final static String RUN_COMMAND = "java -jar javaLatex.jar";

    public static void main(String[] args) {
        System.setProperty("java.util.logging.config.file", "logging.properties");

        if (args.length > 0) {
            convert(args);
        } else {
            System.out.println("No arguments supplied. Syntax:\n" +
                    "\t" + RUN_COMMAND + " --option1 --option2 --etc\n" +
                    "You probably want\n" +
                    "\t" + RUN_COMMAND + " --title=\"Book Title\" --genall\n" +
                    "where Book Title is the title of the book to be converted, or:\n" +
                    "\t" + RUN_COMMAND + " --input=input_file --output=output_file\n" +
                    "to convert single file containing MediaWiki markup.\n\n" +
                    "Type\n\t" + RUN_COMMAND + " --help\nto get more help.");
        }
    }

    /**
     * Converts a book, given the command line argument settings. Uses default settings, which are
     * overwritten by the configuration file settings, and the command-line argument settings,
     * respectively.
     * 
     * @param args Command-line arguments. (ie <code>CommandLineConverter.convert("title=Book Title", "--genall")</code>)
     */
    public static void convert(String... args) {

        // read the settings
        BookSettings bs = new BookSettings("Wikibook");
        ConversionSettings cs = new ConversionSettings();

        readConfigurationFromFiles(bs, cs);
        getBookSettings(bs, args);
        getConversionSettings(cs, args);

        // process the book as set-up
        if (bs.inputFile != null) {
            String wikicode = Util.getStringFromFile(bs.inputFile);
            wikicode = new WikicodeConverterImpl(bs, cs, bs.inputFile).convert(wikicode);
            Util.writeToFile(wikicode, bs.outputFile);
        } else if (bs.article != null) {
            bs.generateAuthorsList = true;
            bs.generateMainFile = true;
            bs.generateStyle = true;
            bs.generateTitlePage = false;
            bs.generateChapters = true;
            bs.generateDocInfo = true;
            bs.getImages = true;
            bs.generateChapterLinks = true;
            bs.addChapters(bs.article);
            bs.setTitle("");
            bs.setPath(String.format("LaTeX/%s LaTeX/", bs.article));
            bs.setDisplayTitle(bs.article);
            cs.headingLevelModifier = 1;

            new WikibookConverter(bs, cs).convertBook();
        } else {

            new WikibookConverter(bs, cs).convertBook();
        }

        Log.write();
    }

    /**
     * Read conversion and book server settings from the configuration file ("conversion.properties"),
     * altering the original given settings.
     *
     * @param bs the book settings to be altered
     * @param cs the conversion settings to be altered
     */
    private static void readConfigurationFromFiles(BookSettings bs, ConversionSettings cs){

        Properties conversionProperties = new Properties();

        try {
            conversionProperties.load(new FileInputStream("conversion.properties"));
        } catch (IOException e) {
            Log.addWarning("cannot read file 'convertions.properties', reverting to default: " + e.getLocalizedMessage());
            return; // proceed with default settings in case of an error
        }

        bs.setServer(conversionProperties.getProperty("server"));
        bs.setLanguage(conversionProperties.getProperty("language"));
        bs.setMainDir(conversionProperties.getProperty("main_dir"));
        
        Util.setServer(bs.getServer());
        Util.setMainDir(bs.getMainDir());

        String strVal = conversionProperties.getProperty("logo_path");
        cs.logoPath = strVal != null ? strVal : cs.logoPath;
        
        strVal = conversionProperties.getProperty("convertCodeTo");
        cs.convertCodeToPre = strVal != null ? "verbatim".equalsIgnoreCase(strVal) : cs.convertCodeToPre;

        Boolean boolVal = configParseBool(conversionProperties, "smallerMargins");
        cs.smallerMargins = boolVal != null ? boolVal : cs.smallerMargins;

        strVal = conversionProperties.getProperty("nbsp");
        cs.nbsp = strVal != null ? strVal : cs.nbsp;

        boolVal = configParseBool(conversionProperties, "noWrapFigs");
        cs.noWrapFigs = boolVal != null ? boolVal : cs.noWrapFigs;

        Integer intVal = configParseNumber(conversionProperties, "headingLevelModifier");
        cs.headingLevelModifier = intVal != null ? intVal : cs.headingLevelModifier;

        intVal = configParseNumber(conversionProperties, "imageDPI");
        cs.imageDPI = intVal != null ? intVal : cs.imageDPI;

        intVal = configParseNumber(conversionProperties, "textWidth");
        cs.textWidth = intVal != null ? intVal : cs.textWidth;

        strVal = conversionProperties.getProperty("imageDefaultWidth");
        cs.imageDefaultWidth = strVal != null ? strVal : cs.imageDefaultWidth;
    }

    /**
     * Sets fields in class BookSettings from command-line arguments, altering the previous settings.
     *
     * @param bs the book settings with default or config file values
     * @param args Command-line arguments. (ie <code>CommandLineConverter.getBookSettings("title=Book Title",
     *              "--genall")</code>)
     */
    private static void getBookSettings(BookSettings bs, String... args) {

        for (String arg : args) {
            String value = null;
            if (arg.contains("=")) {
                value = arg.substring(arg.indexOf("=") + 1);
            }

            if (arg.contains("--help") || arg.equals("-h")) {
                printHelp();
                System.exit(0);
            }

            if (arg.contains("--title=")) {
                bs.setTitle(value);
                continue;
            }
            if (arg.contains("--author=")) {
                bs.setAuthor(value);
                continue;
            }

            if (arg.contains("--path=")) {
                bs.setPath(value);
                continue;
            }

            if (arg.contains("--chapterlistlocation=") || arg.contains("--cll=")) {
                bs.setChapterListLocation(value);
                continue;
            }

            if (arg.equals("--createchaptersfromfile") || arg.equals("--ccff")) {
                bs.createChaptersFromFile = true;
                continue;
            }

            if (arg.equals("--tocfromfile")) {
                bs.readTocFromFile = true;
                continue;
            }

            if (arg.contains("--displaytitle=") || arg.contains("--dt=")) {
                bs.setDisplayTitle(value);
                continue;
            }

            if (arg.contains("--input")) {
                bs.inputFile = value;
                continue;
            }

            if (arg.contains("--output")) {
                bs.outputFile = value;
                continue;
            }

            if (arg.contains("--article")) {
                bs.article = value;
                continue;
            }

            // shortcuts
            if (arg.charAt(0) == '-' && arg.charAt(1) != '-') {
                if (arg.contains("c")) {
                    bs.generateChapters = true;
                    continue;
                }
                if (arg.contains("m")) {
                    bs.generateMainFile = true;
                    continue;
                }
                if (arg.contains("t")) {
                    bs.generateTitlePage = true;
                    continue;
                }
                if (arg.contains("l")) {
                    bs.useTitlePageLogo = true;
                    continue;
                }
                if (arg.contains("s")) {
                    bs.generateStyle = true;
                    continue;
                }
                if (arg.contains("d")) {
                    bs.generateDocInfo = true;
                    continue;
                }
                if (arg.contains("a")) {
                    bs.generateAuthorsList = true;
                    continue;
                }
                if (arg.contains("k")) {
                    bs.generateKileProjectFile = true;
                    continue;
                }
                if (arg.contains("w")) {
                    bs.generateWikiPage = true;
                    continue;
                }
                if (arg.contains("i")) {
                    bs.getImages = true;
                    continue;
                }
            }

            boolean doContinue = false;

            if (arg.equals("--genchapters") || arg.equals("--genall")) {
                bs.generateChapters = true;
                doContinue = true;
            }
            if (arg.equals("--genmainfile") || arg.equals("--genall")) {
                bs.generateMainFile = true;
                doContinue = true;
            }
            if (arg.equals("--gentitlepage") || arg.equals("--genall")) {
                bs.generateTitlePage = true;
                doContinue = true;
            }
            if (arg.equals("--usetitlepagelogo") || arg.equals("--genall")) {
                bs.useTitlePageLogo = true;
                doContinue = true;
            }
            if (arg.equals("--genstyle") || arg.equals("--genall")) {
                bs.generateStyle = true;
                doContinue = true;
            }
            if (arg.equals("--gendocinfo") || arg.equals("--genall")) {
                bs.generateDocInfo = true;
                doContinue = true;
            }
            if (arg.equals("--genauthorslist") || arg.equals("--genall")) {
                bs.generateAuthorsList = true;
                doContinue = true;
            }
            if (arg.equals("--genkilepr") || arg.equals("--genall")) {
                bs.generateKileProjectFile = true;
                doContinue = true;
            }
            if (arg.equals("--genwikipage") || arg.equals("--genall")) {
                bs.generateWikiPage = true;
                doContinue = true;
            }
            if (arg.equals("--getimages") || arg.equals("--genall")) {
                bs.getImages = true;
                doContinue = true;
            }
            if (arg.equals("--genchapterlinks")) { // not in --genall !
                bs.generateChapterLinks = true;
                doContinue = true;
            }

            // chapters: command-line input format: "filename.tex|Chapter Title"
            if (arg.contains(".txt|") || arg.contains(".tex|")) {
                String filename = arg.substring(0, arg.indexOf("."));
                String chapter = arg.substring(arg.indexOf("|") + 1);
                bs.addChapterFromFile(filename, chapter);
                continue;
            }

            if (doContinue) {
                continue;
            }

            System.err.println("unknown argument: " + arg);
        }

        if (bs.chapterListLocation().equals("Wikibook")) {
            bs.setChapterListLocation(bs.title());
        }

        if (bs.path().isEmpty()){
            if (!bs.title().isEmpty()){
                bs.setPath("LaTeX/" + bs.title() + " LaTeX/");
            }
            else if(!bs.getDisplayTitle().isEmpty()){
                bs.setPath("LaTeX/" + bs.getDisplayTitle() + " LaTeX/");
            }
            else {
                bs.setPath("LaTeX/" + "Wikibook LaTeX/");
            }
        }

        if (bs.getDisplayTitle() == null || bs.getDisplayTitle().equals("")) {
            bs.setDisplayTitle(bs.title());
        }
    }

    /**
     * prints out help about the application to standard output.
     */
    private static void printHelp() {
        System.out.println("javaLatex - converts MediaWiki code to LaTeX markup\n" +
                "Syntax: " + RUN_COMMAND + " --option1 --option2\n");
        System.out.println("Most important options:");
        System.out.println(" --input=file.txt      Converts single file and finishes.");
        System.out.println(" --output=file.tex     Name of output file when single file mode is active.");
        System.out.println("                       Defaults to out.tex.");
        System.out.println(" --article=Article     Converts single article from wiki and finishes.");
        System.out.println(" --title=\"Book title\"  Title of wikibook to convert.");
        System.out.println(" --genall              Downloads images, generates TOC and many");
        System.out.println("                       useful information");
        System.out.println(" --tocfromfile         Reads table of contents from file [Book title].txt.");
        System.out.println(" --displaytitle=\"foo\"  Sets another title displayed on book title page.");
        System.out.println("\nMany important options are set in conversion.properties file, which you should " +
                "edit before running the program.");
    }

    /**
     * Sets fields in class ConversionSettings from command-line arguments, altering the previous settings.
     * 
     * @param cs the conversion settings with values set from the config file / default values
     * @param args Command-line arguments.
     */
    private static void getConversionSettings(ConversionSettings cs, String... args) {

        for (String arg : args) {
            String value = null;
            if (arg.contains("=")) {
                value = arg.substring(arg.indexOf("=") + 1);
            }

            if (arg.contains("--indent=")) {
                cs.setIndent(value);
            }
            if (arg.contains("--boldcommand=") || arg.contains("--bc=")) {
                cs.setBoldCommand(value);
            }
            if (arg.contains("--codelanguage=") || arg.contains("--cl=")) {
                cs.setCodeLanguage(value);
            }
            if (arg.contains("--nbsp=")) {
                cs.nbsp = value;
            }

            if ((arg.contains("--converttemplatetocommand=") || arg.contains("--cttc=")) && arg.contains(",")) {
                String templateName = value.substring(0, value.indexOf(","));
                String commandName = value.substring(value.indexOf(",") + 1).trim();
                cs.addToTemplateMap(templateName, commandName);
            }

            if (arg.equals("--silentmode") || arg.equals("--sm")) {
                cs.silentMode = true;
            }

            if (arg.contains("--templateaction=") || arg.contains("--ta=")) {
                cs.setTemplateAction(value);
            }
            if (arg.contains("--spanaction=") || arg.contains("--sa=")) {
                cs.setSpanAction(value);
            }
            if (arg.contains("--divaction=") || arg.contains("--da=")) {
                cs.setDivAction(value);
            }
            if (arg.contains("--imageaction=") || arg.contains("--ia=")) {
                cs.setImageAction(value);
            }
            if (arg.equals("--nowrapfigs") || arg.equals("--nwf")){
                cs.noWrapFigs = true;
            }
        }
    }

    /**
     * Returns a numeric value read from the configuration file, or null if not set or error.
     * Issues warning if the value is set incorrectly.
     *
     * @param config the conversion properties settings
     * @param propertyName name of the numeric property to test
     * @return the numeric value, or null
     */
    private static Integer configParseNumber(Properties config, String propertyName) {

        try {
            if (config.getProperty("propertyName") == null){
                return null;
            }
            return Integer.parseInt(config.getProperty(propertyName));
        }
        catch (NumberFormatException e) {
            Logger.getLogger("LaTeXConverter").warning(propertyName + " from configuration file is not a " +
                    "valid number, using defaults.");
            return null;
        }
    }

    /**
     * Returns a boolean value from the configuration file, or null if not set.
     *
     * @param config the conversion properties settings
     * @param propertyName name of the boolean property to test
     * @return the boolean value, or null
     */
    private static Boolean configParseBool(Properties config, String propertyName) {

        try {
            return config.getProperty(propertyName).equalsIgnoreCase("true");
        }
        catch (NullPointerException e){
            return null;
        }
    }
}
